package com.hyfrogx.common.base;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hyfrogx.common.exception.MyException;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * 基础实现类接口
 *
 * @author wanglei
 * @date 2019-06-06 13:47:46
 */
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {

	/**
	 * 获取主键id
	 *
	 * @return
	 */
	public abstract String getIdKey();

	/**
	 * 获取实体类名称
	 *
	 * @return
	 */
	public abstract String getEntityName();

	@Override
	public <V> V queryVoById(Long id, Class<V> clazz) {
		T entity = this.getById(id);
		if (ObjectUtil.isNull(entity)) {
			throw new MyException(getEntityName() + "不存在");
		}
		V vo = BeanUtil.toBean(entity, clazz);
		return vo;
	}

	@Override
	public <V> List<V> queryVoList(QueryWrapper<T> wrapper, Class<V> clazz) {
		List<T> list = this.list(wrapper);
		return convertToListVo(list, clazz);
	}

	@Override
	public <V> List<V> convertToListVo(List<T> list, Class<V> clazz) {
		if (ObjectUtil.isNull(list)) {
			return null;
		}
		List<V> voList = CollUtil.newArrayList();
		if (CollUtil.isNotEmpty(list)) {
			for (T entity : list) {
				voList.add(BeanUtil.toBean(entity, clazz));
			}
		}
		return voList;
	}

	@Override
	public <V> IPage queryVoPage(IPage<T> iPage, QueryWrapper<T> wrapper, Class<V> clazz) {
		iPage = this.page(iPage, wrapper);
		return convertToPageVo(iPage, clazz);
	}

	@Override
	public <V> IPage<V> convertToPageVo(IPage<T> iPage, Class<V> clazz) {
		List<V> voList = convertToListVo(iPage.getRecords(), clazz);
		Page<V> page = new Page<>(iPage.getCurrent(), iPage.getSize(), iPage.getTotal(), iPage.isSearchCount());
		page.setRecords(voList);
		return page;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public T saveForm(Long userId, Object form) {
		T entity = saveFormToEntity(userId, form);
		return this.saveOrUpdateEntity(entity);
	}

	public T saveFormToEntity(Long userId, Object form) {
		T entity = BeanUtil.toBean(form, this.currentModelClass());
		BeanUtil.setFieldValue(entity, "createUserId", userId);
		BeanUtil.setFieldValue(entity, "createTime", new Date());
		return entity;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public T updateForm(Long userId, Object form) {
		T entity = updateFormToEntity(userId, form);
		return this.saveOrUpdateEntity(entity);
	}

	public T updateFormToEntity(Long userId, Object form) {
		T entity = BeanUtil.toBean(form, this.currentModelClass());
		BeanUtil.setFieldValue(entity, "updateUserId", userId);
		BeanUtil.setFieldValue(entity, "updateTime", new Date());
		return entity;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public T saveOrUpdateEntity(T entity) {
		if (ObjectUtil.isNull(entity)) {
			throw new MyException(getEntityName() + "不能为null");
		}
		initSaveOrUpdateEntity(entity);
		T newEntity;
		String id = Convert.toStr(BeanUtil.getFieldValue(entity, getIdKey()));
		boolean isNew = true;
		if (ObjectUtil.isNull(id)) {
			newEntity = entity;
			beforeSaveEntity(entity);
		} else {
			isNew = false;
			T old = this.getById(id);
			if (ObjectUtil.isNull(old)) {
				throw new MyException(getEntityName() + "已被删除，无法修改");
			}
			newEntity = ObjectUtil.clone(old);
			BeanUtil.copyProperties(entity, newEntity, CopyOptions.create(this.currentModelClass(), true, ArrayUtil.toArray(getUpdateIgnoreKey(), String.class)));
			beforeUpdateEntity(newEntity, old);
		}
		this.beforeSaveOrUpdateEntity(newEntity, isNew);
		if (!this.saveOrUpdate(newEntity)) {
			throw new MyException(getEntityName() + "更新失败");
		}
		this.afterSaveOrUpdateEntity(newEntity, isNew);
		return newEntity;
	}

	public void initSaveOrUpdateEntity(T entity) {
	}

	/**
	 * 创建entity之前的操作
	 *
	 * @param entity
	 */
	public void beforeSaveEntity(T entity) {
		if (ObjectUtil.isNull(entity)) {
			throw new MyException(getEntityName() + "不能为null");
		}
		BeanUtil.setFieldValue(entity, "createTime", new Date());
	}

	/**
	 * 获取更新实体类时不更新的对象
	 *
	 * @return
	 */
	public List<String> getUpdateIgnoreKey() {
		return CollUtil.newArrayList();
	}

	/**
	 * 更新entity之前的操作
	 *
	 * @param entity
	 */
	public void beforeUpdateEntity(T entity, T old) {
		if (ObjectUtil.isNull(entity) || ObjectUtil.isNull(old)) {
			throw new MyException(getEntityName() + "不能为null");
		}
		BeanUtil.setFieldValue(entity, "updateTime", new Date());
	}

	/**
	 * 验证数据有效性
	 *
	 * @param entity
	 * @param isNew
	 */
	public void beforeSaveOrUpdateEntity(T entity, boolean isNew) {
		if (ObjectUtil.isNull(entity)) {
			throw new MyException(getEntityName() + "不能为null");
		}
	}

	public void afterSaveOrUpdateEntity(T entity, boolean isNew) {

	}

	public boolean updatePath(Long id, String oldPath, String parentPath, String pathKey) {
		return updatePath(id, oldPath, parentPath, pathKey, "#", true);
	}

	public boolean updatePath(Long id, String oldPath, String parentPath, String pathKey, String splitStr, Boolean round) {
		if (StrUtil.isBlank(splitStr)) {
			throw new MyException("拼接字符串不能为空");
		}
		List<String> ids = StrUtil.splitTrim(parentPath, splitStr);
		ids.add(Convert.toStr(id));
		String path = CollUtil.join(ids, splitStr);
		if (round) {
			path = StrUtil.format("{}{}{}", splitStr, path, splitStr);
		}
		if (StrUtil.equals(oldPath, path)) {
			return true;
		}
		//本级路由更新
		if (!this.update(
				new UpdateWrapper<T>()
						.set(pathKey, path)
						.eq(getIdKey(), id)
		)) {
			throw new MyException("数据路由更新失败");
		}
		if (StrUtil.isNotEmpty(oldPath)) {
			//下级路由更新
			this.update(
					new UpdateWrapper<T>()
							.setSql(StrUtil.format("{} = REPLACE({},'{}','{}')", pathKey, pathKey, oldPath, path))
							.eq(StrUtil.format("LOCATE('{}',{})", oldPath, pathKey), 1)
							.ne(pathKey, oldPath)
			);
		}
		return true;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean disableByIdList(Long userId, List<Long> idList) {
		return this.updateStatus(userId, idList, false);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean enableByIdList(Long userId, List<Long> idList) {
		return this.updateStatus(userId, idList, true);
	}

	@Override
	public boolean updateStatus(Long userId, List<Long> idList, boolean status) {
		if (CollUtil.isEmpty(idList)) {
			return true;
		}
		beforeUpdateStatus(userId, idList, status);
		T entity = BeanUtil.toBean(null, this.currentModelClass());
		BeanUtil.setFieldValue(entity, "updateUserId", userId);
		BeanUtil.setFieldValue(entity, "updateTime", new Date());
		BeanUtil.setFieldValue(entity, "status", status);
		if (!this.update(entity, new QueryWrapper<T>()
				.in(getIdKey(), idList))) {
			throw new MyException(getEntityName() + "更新状态失败");
		}
		afterUpdateStatus(userId, idList, status);
		return true;
	}

	public void beforeUpdateStatus(Long userId, List<Long> idList, boolean status) {
	}

	public void afterUpdateStatus(Long userId, List<Long> idList, boolean status) {
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean deleteByIdList(Long userId, List<Long> idList) {
		if (CollUtil.isEmpty(idList)) {
			return true;
		}
		beforeDeleteByIdList(userId, idList);
		//删除
		boolean result = this.update(
				new UpdateWrapper<T>()
						.set("delete_user_id", userId)
						.set("delete_time", new Date())
						.set("deleted", true)
						.in(getIdKey(), idList)
		);
		if (!result) {
			throw new MyException("删除" + getEntityName() + "失败");
		}
		afterDeleteByIdList(userId, idList);
		return true;
	}

	public void beforeDeleteByIdList(Long userId, List<Long> idList) {
	}

	public void afterDeleteByIdList(Long userId, List<Long> idList) {
	}
}
